use std::sync::Arc;

use tauri::Window;
use tokio::sync::Mutex;

use crate::error::{new_wrap_err, AppErr, AppErrWrap};

use self::serial_port_conn::SerialPortConn;

mod serial_port_conn;

static PORT: Mutex<Option<Arc<SerialPortConn>>> = Mutex::const_new(None);

#[tauri::command]
pub fn serial_port_names() -> Result<Box<[String]>, AppErr> {
    let infos = tokio_serial::available_ports().wrap_err()?;
    let mut names: Vec<String> = Vec::with_capacity(infos.len());
    for info in infos {
        names.push(info.port_name);
    }
    Ok(names.into_boxed_slice())
}

#[tauri::command]
pub async fn serial_port_is_open() -> bool {
    PORT.lock().await.is_some()
}

#[tauri::command]
pub async fn serial_port_open(window: Window, name: String) -> Result<(), AppErr> {
    serial_port_close().await;
    let mut lock = PORT.lock().await;
    let conn = SerialPortConn::new(&name, window)?;
    *lock = Some(conn);
    Ok(())
}

#[tauri::command]
pub async fn serial_port_write(buf: Box<[u8]>) -> Result<(), AppErr> {
    let mut lock = PORT.lock().await;
    match &mut *lock {
        Some(port) => {
            port.write(&buf).await?;
        }
        None => {
            return new_wrap_err("串口没有打开或者已经关闭".to_string());
        }
    };
    Ok(())
}

#[tauri::command]
pub async fn serial_port_close() {
    let mut lock = PORT.lock().await;
    let port = lock.take();
    if let Some(port) = port {
        port.close();
    }
}
